# -*- coding: utf-8 -*-


from sympy import sympify, Rel, FiniteSet, Union, EmptySet, Set, Interval, Symbol, S, Expr, Integral, simplify, pi, \
    sqrt, Limit
from mathsolver.functions.base.base import BaseValue, new_latex
from mathsolver.functions.sympy_utils import default_symbol


def get_symbol(symbol, exist_symbols):
    index = exist_symbols[symbol]
    exist_symbols[symbol] += 1
    if index == 0:
        return symbol
    else:
        return "%s_%d" % (symbol, index)


class BaseExpr(BaseValue, Expr):
    pass


class BaseIntegral(BaseExpr, Integral):  # Integral("sqrt(x**3+1)", (x, 2, 10))
    def __init__(self, *args):
        Integral.__init__(*args)
        self.equation, (self.var, self.lower_bound, self.upper_bound) = self.args


class OptionSelect(BaseValue):
    pass


class _BaseEq(BaseValue):
    """ 方程式

    BaseEq(["x+1", "y"]).printing()
    'x + 1 = y'
    """

    intermediate = True

    def sympify(self):
        try:
            return [sympify(f, evaluate=False) for f in self.value]
        except Exception:
            return [sympify(f) for f in self.value]

    def printing(self):
        return " = ".join([new_latex(f) for f in self.sympify()])

    def eq2func(self):
        f1, f2 = self.sympify()
        if str(f1) == "y":
            symbol = default_symbol(f2)
            name = "y"
        elif str(f1) == "f(x)":
            symbol = "x"
            name = "f"
        elif "y" in str(f1 - f2):
            a, h = (f1 - f2).as_independent(Symbol("y"))
            index = h.coeff(Symbol("y"))
            f2 = -a / index
            symbol = "x"
            name = "y"
        else:
            raise ValueError()
        return BaseFunc({"name": name, "type": "", "var": symbol, "expression": f2})

    def eq2func_xiaofei(self):
        """把 BaseEq([x^2, x]) 转化成 BaseFunc({'expression': x^2-x})，与 霄然的 toFunc冲突"""
        f1, f2 = self.sympify()
        if str(f1) == "y":
            symbol = default_symbol(f2)
            name = "y"
        elif str(f1) == "f(x)":
            symbol = "x"
            name = "f"
        elif "y" in str(f1 - f2):
            a, h = (f1 - f2).as_independent(Symbol("y"))
            index = h.coeff(Symbol("y"))
            f2 = -a / index
            symbol = "x"
            name = "y"
        else:
            # raise ValueError()
            name = "f"
            expression = f1 - f2
            symbol = default_symbol(expression)
        return BaseFunc({"name": name, "type": "", "var": symbol, "expression": f2})

    def free_symbols(self):
        symbols = set()
        for expr in self.sympify():
            if isinstance(expr, list):
                for ff in expr:
                    symbols.update(ff.free_symbols)
            else:
                symbols.update(expr.free_symbols)

        return symbols


class BaseEq(_BaseEq):
    pass


class BaseDiseq(_BaseEq):
    pass


class BaseSinEq(_BaseEq):
    pass


class _BaseEqs(BaseValue):
    r""" 方程组

    BaseEqs([["x+1", "y"], ["y+2","x"]]).printing()
    '\\begin{equation}\\begin{cases}x + 1 = y\\\\y + 2 = x\\end{cases}\\end{equation}'
    """
    intermediate = True

    def sympify(self):
        eqs = [[sympify(f) for f in eq] for eq in self.value]
        return eqs

    def printing(self):
        latex_eqs = []
        for eq in self.sympify():
            latex_eqs.append("%s = %s" % (new_latex(eq[0]), new_latex(eq[1])))

        latex_eqs = "\\\\".join(latex_eqs)
        return "\\begin{equation}\\begin{cases}%s\\end{cases}\\end{equation}" % latex_eqs

    def free_symbols(self):
        symbols = set()
        for eq in self.sympify():
            for f in eq:
                if isinstance(f, list):
                    for ff in f:
                        symbols.update(ff.free_symbols)
                else:
                    symbols.update(f.free_symbols)

        return symbols


class BaseEqs(_BaseEqs):
    pass


class BaseSinEqs(BaseValue):
    pass


class _BaseIneq(BaseValue):
    """ 不等式"""
    intermediate = True

    def __init__(self, value):
        super(_BaseIneq, self).__init__(value)

    def check(self, symbol_value):
        ineq = self.sympify()
        return Rel(ineq[0], ineq[2], ineq[1]).subs(symbol_value) is True

    def sympify(self):
        f1, op, f2 = self.value
        return sympify(f1), op, sympify(f2)

    def free_symbols(self):
        symbols = set()
        ineq = self.sympify()
        symbols.update(Rel(ineq[0], ineq[2], ineq[1]).free_symbols)
        return symbols

    def subs(self, symbol_values):
        f1, op, f2 = self.sympify()
        f1 = f1.subs(symbol_values)
        f2 = f2.subs(symbol_values)
        self.value = [f1, op, f2]
        return self

    def printing(self):
        f1, op, f2 = self.sympify()
        if op == "!=":
            return "%s %s %s" % (new_latex(f1), "\\ne", new_latex(f2))
        elif op == ">=":
            return "%s %s %s" % (new_latex(f1), "\\ge", new_latex(f2))
        elif op == "<=":
            return "%s %s %s" % (new_latex(f1), "\\le", new_latex(f2))
        else:
            return "%s %s %s" % (new_latex(f1), op, new_latex(f2))


class BaseIneq(_BaseIneq):
    pass


class BaseDisIneq(_BaseIneq):
    pass


class _BaseIneqs(BaseValue):
    """ 不等式组"""
    intermediate = True

    def __init__(self, value):
        super(_BaseIneqs, self).__init__(value)
        # if len(value) == 1 and len(value[0]) :
        #     value = value[0]

        if isinstance(value[0], (list, tuple)):
            self.ineqs = []
            for ineq in self.value:
                if all([op not in str(ineq[1]) for op in "<>!"]):
                    self.ineqs.append(BaseEq(ineq))
                elif len(ineq) == 3:
                    self.ineqs.append(BaseIneq(ineq))
                else:
                    self.ineqs.extend(BaseIneqs(ineq).ineqs)
        else:
            index = 0
            self.ineqs = []
            while index < len(self.value) - 1:
                self.ineqs.append(BaseIneq(value[index:index + 3]))
                index += 2

    def free_symbols(self):
        symbols = set()
        for item in self.ineqs:
            symbols.update(item.free_symbols())
        return symbols

    def sympify(self):
        ineqs = [item.sympify() for item in self.ineqs]
        return ineqs

    def printing(self):
        string = []
        ineq1 = []
        for item in self.ineqs:
            if isinstance(item, BaseIneq):
                ineq = item.sympify()
                if ineq1 and str(ineq1[-1]) == str(ineq[0]):
                    string[-1] += " %s %s" % (ineq[1], new_latex(ineq[2]))
                else:
                    string.append(item.printing())  # += ", %s %s %s" % (latex(ineq[0]), ineq[1], latex(ineq[2]))

                ineq1 = ineq
            else:
                string.append(item.printing())  # += ",%s" %

        if len(string) > 1:
            latex_ineqs = "\\\\".join(string)
            return "\\begin{equation}\\begin{cases}%s\\end{cases}\\end{equation}" % latex_ineqs
        else:
            return string[0]  # .strip(",")

    def subs(self, symbol_value):
        for ineq in self.ineqs:
            ineq.subs(symbol_value)


class BaseIneqs(_BaseIneqs):
    pass


class BaseOr(BaseValue):
    def __new__(cls, value):
        if len(value) == 1:
            return value[0]
        else:
            return BaseValue.__new__(cls)

    def __init__(self, value):
        super().__init__(value)
        self.value = ""
        self.exprs = value


class BaseVariables(BaseValue):
    """ 多个变量"""

    def sympify(self):
        return [sympify(v) for v in self.value]


class BaseAny(BaseValue):
    # def subs(self, symbol_values):
    #     f = self.sympify()
    #     self.value = str(f.subs(symbol_values))
    def free_symbols(self):
        return self.sympify().free_symbols


class BasePoly(BaseAny):
    """ 代数式"""
    intermediate = True

    def subs(self, symbol_value):
        try:
            self.value = self.sympify().subs(symbol_value)
        except Exception:
            pass
        return


class BaseVectorPoly(BaseAny):
    pass


class BaseComplexPoly(BaseAny):
    def free_symbols(self):
        return self.sympify().free_symbols

    def subs(self, symbol_values):
        self.value = self.sympify().subs(symbol_values)
        return self


class BaseSinPoly(BasePoly):
    pass


class BaseInfPoly(BasePoly):
    def sympify(self):
        expr = "+".join(self.value.split("+...+"))
        expr = "+".join(expr.split("-...+"))
        expr = "*".join(expr.split("*...*"))
        expr = expr.replace('*sqrt(...)', '')
        expr = expr.replace('+sqrt(...)', '')
        return sympify(expr, evaluate=False)


class BasePolys(BaseValue):
    """ 多个代数式"""

    def sympify(self):
        return [sympify(v, evaluate=False) for v in self.value]


class BaseCompare(BasePolys):
    pass


class BaseCompares(BaseValue):
    pass


class BaseSymbolValue(BaseValue):
    """ 变量及其对应的值"""

    def __init__(self, value):
        super(BaseSymbolValue, self).__init__(value)
        new_value = {}
        for key, item in value.items():
            # new_value[key] = item
            if isinstance(key, str):
                key = simplify(key)

            if isinstance(item, list):
                new_value[key] = FiniteSet(*item)
                # new_value[key] = item
            elif isinstance(item, Set):
                new_value[key] = item
            else:
                new_value[key] = FiniteSet(item)

        self.value = new_value
        if len(value) == 1 and isinstance(list(value.values())[0], (Interval, Union)):
            self.interval = list(value.values())[0]
        else:
            self.interval = None

    def value2pairs(self):
        pairs = []

        temps1 = [(sym, value) for sym, value in self.value.items() if isinstance(value, FiniteSet)]
        temps2 = [(sym, value) for sym, value in self.value.items() if not isinstance(value, FiniteSet)]

        if temps2 and not temps1:
            pairs.append(dict([(sym, v) for sym, v in temps2]))
        elif temps1:
            symbols, values = zip(*temps1)
            for value in zip(*values):
                pair = {}
                for sym, v in zip(symbols, value):
                    if isinstance(sym, tuple):
                        pair.update(dict([(a, b) for a, b in zip(sym, v)]))
                    else:
                        pair[sym] = v

                pairs.append(pair)

        return pairs

    @staticmethod
    def _output_interval(symbol, value):
        l, r, lopen, ropen = value.args
        smaller = {False: "<=", True: "<"}
        larger = {False: ">=", True: ">"}
        if not (l.is_finite is False) and not (r.is_finite is False):
            return "%s %s %s %s %s" % (new_latex(l), smaller[lopen], new_latex(symbol), smaller[ropen], new_latex(r))
        elif not (l.is_finite is False) and not (r.is_finite is True):
            return "%s %s %s" % (new_latex(symbol), larger[lopen], new_latex(l))
        elif not (r.is_finite is False):
            return "%s %s %s" % (new_latex(symbol), smaller[ropen], new_latex(r))
        else:
            return "%s \\in R" % new_latex(symbol)

    def output_interval(self, symbol, value):
        # if isinstance(value, EmptySet):
        #     return "无解"
        if isinstance(value, Union):
            return new_latex(value)
            # return " \cup ".join([self._output_interval(symbol,arg) for arg in value.args])
        elif isinstance(value, Interval):
            return self._output_interval(symbol, value)
        else:
            return new_latex(value)

    def _printing(self, key, value):
        if isinstance(value, Set):
            return self.output_interval(key, value)
        else:
            if isinstance(value, list):
                if len(value) == 1:
                    return "%s = %s" % (new_latex(key), new_latex(value[0]))
                else:
                    ostr = []
                    for vi, vv in enumerate(value):
                        ostr.append("%s_{%s} = %s" % (new_latex(key), str(vi+1), new_latex(value)))
                    return ",".join(ostr)
            else:
                return "%s = %s" % (new_latex(key), new_latex(value))

    def printing(self):
        out = []
        for pair in self.value2pairs():
            out.append(",".join([self._printing(key, item) for key, item in pair.items()]))

        return " 或 ".join(out)


class BaseTriangleSymbolValue(BaseSymbolValue):
    pass


class BaseNumber(BaseAny):
    """ 数字"""
    pass


class BaseNumbers(BaseValue):
    """ 数字"""
    pass


class BaseVariable(BaseAny):
    """ 变量"""
    intermediate = True

    def __init__(self, value=None):
        super().__init__(value)
        self.value = value

    def sympify(self):
        """ 输出sympy格式 """
        if isinstance(self, Expr):
            return self

        try:
            return sympify(self.value, evaluate=False)
        except Exception:
            return sympify(self.value)

    def printing(self):
        """ 输出latex格式 """
        try:
            return new_latex(self.sympify())
        except Exception:
            return str(self.sympify())

    def get_class_name(self):
        return self.__class__.__name__.lower().replace("base", "")

    def get_input(self, *args):
        return self


class BaseBool(BaseValue):
    """ 布尔值"""

    def sympify(self):
        return self.value


class BaseRootFunc(BaseValue):
    """ 函数"""

    def __init__(self, value):
        super(BaseRootFunc, self).__init__(value)
        if isinstance(value, list):
            value = BaseEq(value).eq2func().value
        self.name = value["name"]
        self.type = value["type"]
        self.var = sympify(value["var"])
        self.values = {}
        self.expression = None
        self.cond = []
        self.dingyiyu = None
        self.constrain = None
        self.update(value)

    def compute(self, *args):
        from mathsolver.functions.hanshu.compute import HanShuCompute
        return HanShuCompute().solver(args[0], self)

    def subs(self, symbol_values):
        if self.expression and not isinstance(self.expression, list):
            expression = self.expression.subs(symbol_values)
            if str(expression) != str(self.expression):
                self.expression = expression
                return True
        self.var = self.var.subs(symbol_values)
        return False

    def free_symbols(self):
        if isinstance(self.expression, list):
            expr_symbols = [f for expr in self.expression for f in sympify(expr["main"]).free_symbols]
        elif not self.expression:
            expr_symbols = []
        else:
            expr_symbols = list(self.expression.free_symbols)

        return set(expr_symbols + list(self.var.free_symbols))

    def update(self, value):
        if "var" in value:
            var = sympify(value["var"])
        else:
            var = self.var

        if "constrain" in value:
            self.constrain = value["constrain"]

        if "expression" in value:
            if self.expression is None:
                self.var = var
                if isinstance(value["expression"], str):
                    self.expression = sympify(value["expression"])
                # 修改：elif isinstance(value["expression"], list) => BasePiecefunc 报错
                elif isinstance(value["expression"], list) and not isinstance(self, BasePieceFunc):
                    self.expression = []
                    for expr in value["expression"]:
                        expr["main"] = sympify(expr["main"])
                        self.expression.append(expr)
                # 分段函数处理
                elif isinstance(value["expression"], list) and isinstance(self, BasePieceFunc):
                    self.expression = []
                    for expr, inter in value["expression"]:
                        from mathsolver.functions.hanshu.helper import check_inter
                        self.expression.append([sympify(expr), check_inter(inter)])
                else:
                    self.expression = value["expression"]

    def sympify(self):
        return Symbol("y"), self.expression

    def printing(self):

        f_name = self.name
        if self.type == 'deri':
            f_name += "'"

        name = "%s(%s)" % (f_name, new_latex(self.var)) if self.name else "y"

        if isinstance(self.expression, list):
            expressions = []
            for expr in self.expression:
                if len(expr["inequality"][0]) == 3:
                    expressions.append("%s,%s" % (new_latex(expr["main"]), BaseIneq(expr["inequality"][0]).printing()))
                else:
                    expressions.append("%s,%s" % (new_latex(expr["main"]), BaseIneqs(expr["inequality"][0]).printing()))
            expressions = "\\\\".join(expressions)
            expressions = "\\begin{equation}\\begin{cases}%s\\end{cases}\\end{equation}" % expressions
            return name + " = %s" % expressions
        else:
            # 补充：deri 类型 printing 结果
            if isinstance(self.dingyiyu, Set):
                dingyiyu = ", " + new_latex(self.dingyiyu)
            elif isinstance(self.dingyiyu, BaseValue):
                dingyiyu = ", " + self.dingyiyu.printing()
            else:
                dingyiyu = ""

            return name + " = %s" % new_latex(self.expression) + dingyiyu


class BaseFunc(BaseRootFunc):
    pass


class BasePieceFunc(BaseFunc):
    """ 分段函数：初始化有2种格式
        # >>> BasePieceFunc({"var": "x", "name": "f", "type": "", "expression": [["x+1", Interval.Ropen(0, S.Infinity)],
                            ["x**2+4*x+1", Interval.open(-S.Infinity, 0)]]}).printing()
        # 'y = x + 1, x belong [0,inf)'
        # 'y = x**2 + 4*x +1, x belong [-inf, 0)'
        # >>> BasePieceFunc({'var': 'x',
                        'expression': [{'main': '1-sqrt(x)',
                                        'equality': [''],
                                        'belong': [''],
                                        'inequality': [['x', '>=', '0']]},
                                      {'main': '(2)**(x)',
                                        'equality': [''],
                                        'belong': [''],
                                        'inequality': [['x', '<', '0']]}],
                                        'type': '', 'name': 'f'}})
    """

    def __init__(self, value):
        # 初始化处理
        expression = value['expression']
        from mathsolver.functions.hanshu.helper import check_inter
        if isinstance(expression[0], dict):
            expr_list = []
            for item in expression:
                expr = item['main']
                if item['inequality'][0]:
                    inter = check_inter(BaseIneq(item['inequality'][0]))
                elif item['belong'][0]:
                    belong = item['belong'][0]
                    inter = check_inter(BaseBelong([belong[0], belong[1], belong[2]]))
                else:
                    raise RuntimeError('需要补充其他类型')
                expr_list.append([expr, inter])
            value.update({'expression': expr_list})
        super(BasePieceFunc, self).__init__(value)

    def subs(self, symbol_values):
        new_expression = []
        for key, value in self.expression:
            new_expression.append([key.subs(symbol_values), value.subs(symbol_values)])
        self.expression = new_expression
        self.var = self.var.subs(symbol_values)
        return True

    def sympify(self):
        funs = []
        f, exp = self.name, self.expression
        for e in exp:
            expr = sympify(e[0], evaluate=False)
            funs.append([f, expr, e[1]])
        return funs

    def free_symbols(self):
        symbols = set()
        for y, expr, domain in self.sympify():
            symbols.update(y.free_symbols)
            symbols.update(expr.free_symbols)

    def run(self, value):
        for expr, interval in self.expression:
            if value in interval:
                return sympify(expr).subs(self.var, value)
        raise RuntimeError('cannot solve run %s in piecewise function substitution' % value)

    def printing(self):
        p = ''
        for f1, f2, domain in self.sympify():
            p += " %s = %s , %s ∈ %s\n" % (new_latex(f1), new_latex(f2), new_latex(f2.free_symbols.pop()),
                                           new_latex(domain))
        return p


class BaseFuncNum(BaseFunc):
    pass


class BaseFuncExpr(BaseFunc):
    pass


class BaseCond(BaseValue):
    """ 条件"""

    def __init__(self, value):
        super(BaseCond, self).__init__(value)
        self.equality = value["equality"]
        self.belong = value["belong"]
        self.inequality = value["inequality"]

    def get_cond(self):
        return ([BaseEq(t) for t in self.equality] + [BaseBelong(t) for t in self.belong] +
                [BaseIneq(t) for t in self.inequality])


class BaseBelong(BaseValue):
    """ 表达式：属于
    Example:
        BaseBelong([x, "\\in", Interval(0, 1)])
    """

    def __init__(self, value):
        super(BaseBelong, self).__init__(value)
        try:
            self.var = sympify(value[0])
            inter = value[2]
            self.interval = BaseInter(inter).interval if isinstance(inter, list) else inter
        except Exception:
            self.var = None

    def __hash__(self):
        return hash(self.interval) ^ hash(self.var)

    def __eq__(self, other):
        if type(self) == type(other):
            return self.var == other.var and self.interval == other.interval
        else:
            return False

    def printing(self):
        if self.value[1] == '\u2209':
            return new_latex(self.var) + "\\notin " + new_latex(self.interval)
        else:
            return new_latex(self.var) + "\\in " + new_latex(self.interval)


class BaseBelongs(BaseValue):
    """表达式：多个属于（单个属于用 BaseBelong）
    Example:
        BaseBelongs([['x_1', '\u2208', ['[', '1', 'E', ']']],
                     ['x_2', '\u2208', ['[', '1', 'E', ']']]])"""

    def __init__(self, value):
        super(BaseBelongs, self).__init__(value)

        try:
            varbs, _, inters = zip(*value)

            self.var = sympify(varbs)
            self.interval = []
            for inter in inters:
                if isinstance(inter, list):
                    self.interval.append(BaseInter(inter).interval)
                else:
                    self.interval.append(inter)
        except Exception:
            self.var = None

    @property
    def get_var_interval_dict(self):
        return dict(zip(self.var, self.interval))

    def __hash__(self):
        return hash(self.interval) ^ hash(self.var)

    def __eq__(self, other):
        return self.var == other.var and self.interval == other.interval

    def printing(self):
        str_coll = [new_latex(var) + "\\in " + new_latex(interval) for var, interval in
                    self.get_var_interval_dict.items()]
        return ', '.join(str_coll)


class BaseInter(BaseValue):
    """ 区间表示
    Example:
        BaseInter([["[","(-1)","(-(1)/(2))",")"],"\u222a",["[","(-1)","(-(1)/(2))",")"]])
    """

    def __init__(self, value):
        super(BaseInter, self).__init__(value)
        if isinstance(value, dict):
            # 若函数f(x)=cos2x+asinx在区间(\\frac{π}{6},\\frac{π}{2})是减函数,则a的取值范围是().
            value = value['value']
            interval = self.to_interval(["("] + value + [")"])
            self.value = value
        elif isinstance(value, Set):
            interval = value
        elif "∪" in value:
            sub_interval = [self.to_interval(item) for item in value if item != "∪"]
            interval = sub_interval[0]
            for sub in sub_interval[1:]:
                interval = interval.union(sub)
        elif value == "R":
            interval = S.Reals
        else:
            interval = self.to_interval(value)
        self.interval = interval

    @staticmethod
    def to_interval(interval):
        args = []
        for arg in interval[1:3]:
            if arg == "∞":
                args.append(S.Infinity)
            elif arg == "-∞":
                args.append(-S.Infinity)
            else:
                args.append(sympify(arg))

        return Interval(args[0], args[1], interval[0] == "(", interval[-1] == ")")

    def printing(self):
        return new_latex(self.interval)


class BasePoint(BaseValue):
    """ 点"""

    def __init__(self, value):
        super().__init__(value)
        if isinstance(value, dict):
            self.value = value["value"]
            self.name = value["name"]
        elif isinstance(value, (tuple, list)):
            self.value = value
            self.name = ""
        else:
            raise ValueError()

    def sympify(self, evaluate=False):
        f1, f2 = self.value
        f1 = sympify(f1)
        f2 = sympify(f2)
        return f1, f2

    def to_inter(self):
        return BaseInter(["("] + self.value + [")"])

    def printing(self):
        f1, f2 = self.sympify()
        return "%s(%s,%s)" % (self.name, new_latex(f1), new_latex(f2))

    def subs(self, value):
        f1, f2 = self.sympify()
        f1 = f1.subs(value)
        f2 = f2.subs(value)
        self.value = [f1, f2]
        return self

    def free_symbols(self):
        return set([s for f in self.sympify() for s in f.free_symbols])


class BasePoints(BaseValue):
    """ 多个点"""

    def sympify(self):
        return [BasePoint(v).sympify() for v in self.value]

    def printing(self):
        return ",".join([BasePoint(v).printing() for v in self.value])

    def free_symbols(self):
        return set([s for v in self.value for s in BasePoint(v).free_symbols()])


class BaseSinFunc(BaseRootFunc):
    pass


class BaseFuncName(BaseRootFunc):
    """ 函数名

    BaseFuncName({"name":"f", "type":"", "var":"x"}).printing()
    'f(x)'
    """

    def printing(self):
        if self.type == "inverse":
            flag = "^{-1}"
        elif self.type == "deri":
            flag = "'"
        else:
            flag = ""

        return "%s%s(%s)" % (self.name, flag, new_latex(self.var))


class BaseFuncNames(BaseValue):
    """
    BaseFuncNames([{"var": "-2", "name":"f", "type":""},
                   {"var": "-pi", "name":"f", "type":""},
                   {"var": "3", "name":"f", "type":""}
                 ])
    """

    @property
    def get_value(self):
        return [BaseFuncName(item) for item in self.value]


class BaseFuncPoly(BaseFuncName):
    pass


class BaseFuncNameD(BaseFuncName):
    pass


class BaseFuncNameInverse(BaseFuncName):
    pass


class BaseFuncEq(_BaseEq):
    pass


class BaseFuncAssign(BaseValue):
    def __init__(self, value):
        super().__init__(value)
        self.value = value
        self.assign = {sympify(value["var"]): sympify(value["expression"])}

    def printing(self):
        return ""


class BaseIncomEq(BaseEq):
    """ 不完整的方程 """
    pass


class BaseSet(BaseValue):
    """ 集合 """

    def free_symbols(self):
        return set()


class BaseSets(BaseValue):
    """ 多个集合 """
    pass


class BaseFuncs(BaseValue):
    """ 多个函数 """

    def sympify(self):
        funcs = [BaseFunc(val) for val in self.value]
        return [func.sympify() for func in funcs]

    def printing(self):
        funcs = [BaseFunc(val) for val in self.value]
        return ", ".join([func.printing() for func in funcs])


class BaseSetOp(BaseValue):
    """ 集合运算 """
    pass


class BaseSetOps(BaseValue):
    """ 多个集合运算 """
    pass


class BaseSetEq(BaseValue):
    """ 多个集合运算 """
    pass


class BaseInnerSet(BaseValue):
    """ 内部集合 """

    def __init__(self, value):
        super().__init__(value)
        self.value = value
        self.name = self.value["name"]

    def subs(self, symbol_value):
        value = self.value["value"]
        if isinstance(value, list):
            new_value = [sympify(v).subs(symbol_value) for v in value]
        elif isinstance(value, Set):
            new_value = value.subs(symbol_value)
        else:
            return False
        if value != new_value:
            self.value["value"] = new_value
            return True

        return False

    def free_symbols(self):
        value = self.value["value"]
        if isinstance(value, BaseValue) and hasattr(value, "free_symbols"):
            return value.free_symbols()
        elif hasattr(value, "free_symbols"):
            return value.free_symbols
        elif isinstance(value, list):
            return set([sym for v in value for sym in v.free_symbols])
        else:
            return set()

    def printing(self):
        name = self.value["name"]
        var = self.value["var"]
        value = self.value["value"]
        if isinstance(var, list):
            var = "(%s)" % ",".join([new_latex(sympify(v)) for v in self.value["var"]])

        if isinstance(value, list):
            output = ",".join([new_latex(sympify(v)) for v in value])
            output = "\\{%s\\}" % output
        elif isinstance(value, BaseSymbolValue):
            output = "\\{%s\\}" % value.printing()

        elif value == EmptySet:
            output = "∅"

        elif isinstance(value, Set):
            output = new_latex(value)

        else:
            output = value.printing() if isinstance(value, BaseValue) else new_latex(value)
            var = "%s|" % var if var else ""
            output = "\\{%s\\}" % (var + output)

        name = "%s = " % name if name else ""
        return "%s%s" % (name, output)


class BaseQuestion(object):
    intermediate = True

    def __init__(self, value, main):
        self.value = value
        self.main = main

    def get_input(self):
        return self.value

    def to_query(self):
        return self.main


class BaseCondition(BaseQuestion):
    def __init__(self, value, main, is_or=False):
        super().__init__(value, main)
        self.value = value
        self.main = main
        self.is_or = is_or


class FailException(Exception):
    pass


class BaseObject(object):
    def sympify(self):
        pass


class BaseExpression(BaseValue):
    obj = {}

    def free_symbols(self):
        return []


class BasePointNames(BaseValue):
    pass


def base_gen(value):
    # TODO: BaseVariable cannot be identified against BaseEq
    # TODO: x= y = z cannot be identified against x > y

    if isinstance(value, list):
        if isinstance(value[0], list) or isinstance(value[0], tuple):
            if len(value) > 1:
                if all([len(eq) == 2 for eq in value]):
                    base_object = BaseEqs(value)
                else:
                    base_object = BaseIneqs(value)
            elif len(value[0]) == 2:
                base_object = BaseEq(value[0])
            else:
                base_object = BaseIneq(value[0])
        elif len(value) == 2:
            base_object = BaseEq(value)
        else:
            base_object = BaseIneq(value)

    elif isinstance(value, dict):
        return BaseSymbolValue(value)
    else:
        if len(value.free_symbols) == 0:
            base_object = BaseValue(value)
        else:
            base_object = BasePoly(value)

    base_object.sympify()  # test
    return base_object


class BaseString(BaseValue):
    pass


class BaseGraph(BaseEq):
    intermediate = False

    def __init__(self, value):
        super().__init__(value)
        self.cond = None
        if isinstance(value, list):
            self.value = value
            self.name = ""
        elif isinstance(value, dict):
            self.value = value["value"]
            self.name = value["name"]
        else:
            raise ValueError(value)

    def free_symbols(self):
        if self.value:
            f1, f2 = self.sympify()
            return list(f1.free_symbols) + list(f2.free_symbols)
        else:
            return []

    def sympify(self):
        f1, f2 = self.value
        f1 = sympify(f1, evaluate=False)
        f2 = sympify(f2, evaluate=False)
        return f1, f2

    def printing(self):
        f1, f2 = self.sympify()
        if self.name:
            return "%s: %s = %s" % (self.name, new_latex(f1), new_latex(f2))
        else:
            return "%s = %s" % (new_latex(f1), new_latex(f2))

    def subs(self, value):
        f1, f2 = self.sympify()
        f1 = f1.subs(value)
        f2 = f2.subs(value)
        self.value = [f1, f2]
        return self


class BaseZhiXian(BaseGraph):
    pass


class BaseAxis(BaseEq):
    """ 象限 """
    name = ""

    def __init__(self, value):
        super().__init__(value)
        if value == "y轴":
            self.value = ["x", "0"]
        elif value == "x轴":
            self.value = ["y", "0"]
        else:
            raise ValueError("unexpected %s" % self.value)


class BaseCurve(BaseGraph):
    pass


class BaseName(BaseValue):
    def init(self, exist_symbols):
        obj_name = self.__class__.__name__
        if obj_name == "BaseLineName":
            k = get_symbol("k", exist_symbols)
            d = get_symbol("d", exist_symbols)
            return BaseZhiXian({"name": self.value, "value": ["%s*y" % k, "x + %s" % d]}), []
        elif obj_name == "BaseNZhiXianName":
            k = get_symbol("k", exist_symbols)
            d = get_symbol("d", exist_symbols)
            return BaseZhiXian({"name": self.value, "value": ["y", "%s*x + %s" % (k, d)]}), []
        elif obj_name == "BaseYuanName":
            a = get_symbol("a", exist_symbols)
            b = get_symbol("b", exist_symbols)
            r = get_symbol("r", exist_symbols)
            return BaseYuan({"name": self.value, "value": ["(x-%s)**2 + (y-%s)**2" % (a, b), r]}), []
        elif obj_name == "BasePointName":
            x = get_symbol("x", exist_symbols)
            y = get_symbol("y", exist_symbols)
            return BasePoint({"name": self.value, "value": [x, y]}), []
        elif obj_name == "BaseTuoYuanName":
            a = get_symbol("a", exist_symbols)
            b = get_symbol("b", exist_symbols)
            return BaseTuoYuan({"name": self.value, "value": ["x**2/(%s)**2 + y**2/(%s)**2" % (a, b), "1"]}), [
                [a, ">", b], [b, ">", "0"]]
        elif obj_name == "BaseShuangQuName":
            a = get_symbol("a", exist_symbols)
            b = get_symbol("b", exist_symbols)
            return BaseShuangQu({"name": self.value, "value": ["x**2/(%s)**2 - y**2/(%s)**2" % (a, b), "1"]}), [
                [a, ">", "0"], [b, ">", "0"]]
        elif obj_name == "BaseDzShuangQuName":
            a = get_symbol("a", exist_symbols)
            return BaseShuangQu({"name": self.value, "value": ["x**2/(%s)**2 - y**2/(%s)**2" % (a, a), "1"]}), [
                [a, ">", "0"]]
        elif obj_name == "BasePaoWuXianName":
            p = get_symbol("p", exist_symbols)
            return BasePaoWuXian({"name": self.value, "value": ["y**2", "2*(%s)*x" % p]}), [["p", ">", "0"]]
        elif obj_name == "BaseGraphname":
            return BaseGraph({"name": self.value, "value": ["", ""]}), []
        else:
            raise ValueError()

    def free_symbols(self):
        return []


class BaseGraphname(BaseName):
    pass


class BasePointName(BaseName):
    pass


class BaseNZhiXianName(BaseName):
    pass


class BaseLineName(BaseName):
    pass


class BaseYuan(BaseGraph):
    def to_point(self):
        from mathsolver.functions.yuan import GetYuanCenterPoint
        return GetYuanCenterPoint().solver(self).output[0]


class BasePartial(BaseGraph):
    pass


class BaseYuanName(BaseName):
    pass


class BaseZhiXianName(BaseName):
    pass


class BaseTuoYuan(BaseGraph):
    pass


class BaseShuangQu(BaseGraph):
    pass


class BaseDzShuangQuName(BaseName):
    pass


class BaseTuoYuanName(BaseName):
    pass


class BaseShuangQuName(BaseName):
    pass


class BasePaoWuXian(BaseGraph):
    pass


class BasePaoWuXianName(BaseName):
    pass


class BaseZhiXians(BaseValue):
    """ 多个直线"""

    def sympify(self):
        return [BaseZhiXian(v).sympify() for v in self.value]

    def printing(self):
        return ",".join([BaseZhiXian(v).printing() for v in self.value])


class BaseDu(BaseAny):
    pass


class BaseDistance(BaseValue):
    pass


class BaseVector(BaseValue):
    """向量"""
    # def __init__(self, value=None):
    #     super(BaseVector, self).__init__(value)
    #     self.value = value["value"]
    #     self.name = value["name"]

    def sympify(self):
        if len(self.value) == 2:
            f1, f2 = self.value
            f1 = sympify(f1, evaluate=False)
            f2 = sympify(f2, evaluate=False)
            return f1, f2
        else:
            f1, f2, f3 = self.value
            f1 = sympify(f1, evaluate=False)
            f2 = sympify(f2, evaluate=False)
            f3 = sympify(f3, evaluate=False)
            return f1, f2, f3

    def printing(self):
        if len(self.value) == 2:
            f1, f2 = self.sympify()
            return "(%s,%s)" % (new_latex(f1), new_latex(f2))
        else:
            f1, f2, f3 = self.sympify()
            return "(%s,%s,%s)" % (new_latex(f1), new_latex(f2), new_latex(f3))

    def __add__(self, other):
        if len(self.value) == 2:
            f1, f2 = self.sympify()
            f3, f4 = other.sympify()
            self.value = [f1 + f3, f2 + f4]
        else:
            f1, f2, f3 = self.sympify()
            f4, f5, f6 = other.sympify()
            self.value = [f1 + f4, f2 + f5, f3 + f6]
        return self

    def __mul__(self, other):
        if len(self.value) == 2:
            f1, f2 = self.sympify()
            if isinstance(other, BaseVector):
                f3, f4 = other.sympify()
                return str(f1 * f3 + f2 * f4)
            else:
                f1, f2 = f1 * sympify(other), f2 * sympify(other)
                self.value = [f1, f2]
        else:
            f1, f2, f3 = self.sympify()
            if isinstance(other, BaseVector):
                f4, f5, f6 = other.sympify()
                return str(f1 * f4 + f2 * f5 + f3 * f6)
            else:
                f1, f2, f3 = f1 * sympify(other), f2 * sympify(other), f3 * sympify(other)
                self.value = [f1, f2, f3]
        return self

    def __sub__(self, other):
        if len(self.value) == 2:
            f1, f2 = self.sympify()
            f3, f4 = other.sympify()
            self.value = [f1 - f3, f2 - f4]
        else:
            f1, f2, f3 = self.sympify()
            f4, f5, f6 = other.sympify()
            self.value = [f1 - f4, f2 - f5, f3 - f6]
        return self

    def __truediv__(self, other):
        f1, f2 = self.sympify()
        if isinstance(other, (str, int)):
            f1, f2 = f1 / sympify(other), f2 / sympify(other)
            self.value = [f1, f2]
        return self


class BaseVectorname(BaseVector):
    def __add__(self, other):
        assert self.value[1] == other.value[0]
        self.value = self.value[0] + other.value[1]
        return self

    def __sub__(self, other):
        assert self.value[0] == other.value[0]
        self.value = other.value[1] + self.value[1]
        return self

    def printing(self):
        return "\\overrightarrow{%s}" % self.value


class BaseValues(BaseValue):
    """ 多个变量"""

    def sympify(self):
        return [sympify(v) for v in self.value]


class BaseShanXing(BaseValue):
    S, C, L, R, A = None, None, None, None, None  # 面积S,周长C,弧长L,半径R，圆心角A


class BaseComplexEq(_BaseEq):
    pass


class BaseInnerSetvariable(BaseAny):
    pass


class BaseInnerSeteq(_BaseEq):
    pass


class BaseInnerSeteqs(_BaseEqs):
    pass


class BaseInnerSetpoly(BaseValue):
    pass


class BaseInnerSetpolys(BaseValue):
    pass


class BaseInnerSetinfpoly(BaseValue):
    pass


class BaseInnerSetvariables(BaseValue):
    pass


class BaseVectorEqs(_BaseEqs):
    pass


class BaseVectorEq(_BaseEq):
    pass


class BaseVectors(BaseValue):
    pass


class BaseSinPolys(BasePolys):
    pass


class BaseMultiple(BaseValue):
    def __init__(self, inp):
        super().__init__()
        self.objs = inp["objs"]
        self.value = [obj.value for obj in self.objs]
        self.type = inp["type"]

    def sympify(self):
        return [v.sympify() for v in self.objs]

    def free_symbols(self):
        return [sym for v in self.objs for sym in v.free_symbols()]

    def sub(self, symbol_value):
        for v in self.objs:
            v.sub(symbol_value)
        return self

    def printing(self):
        return ",".join([v.printing() for v in self.objs])


class BaseOptimize(BaseValue):
    def __init__(self, value, flag, cond=None):
        super().__init__(value)
        self.value = value
        self.flag = flag
        self.cond = cond

    def free_symbols(self):
        return self.sympify().free_symbols


class BaseQuadra(BaseValue):
    pass


class BaseSiBian(BaseQuadra):
    pass


class BasePieceWiseFunc(BaseValue):
    pass


class BaseAngle(BaseValue):
    pass


class BaseInnerSetuence(BaseValue):
    """ 数列 """

    def __init__(self, value):
        super(BaseInnerSetuence, self).__init__(value)
        new_value = sympify(value)
        name = new_value.func
        index = new_value.args[0]
        self.name = str(name)  # 数列名
        self.n = sympify(index)  # 数列下标


class BaseComEq(_BaseEq):
    pass


class BaseSum(BaseAny):
    pass


class BaseLiTi(BaseValue):
    def init(self, exist_symbols):
        return self


class BaseLengZhu(BaseLiTi):
    pass


class BaseZhiLengZhu(BaseLengZhu):
    pass


class BaseSiLengZhu(BaseLengZhu):
    a = None
    b = None
    c = None

    def subs(self, symbol_value):
        self.a = self.a.subs(symbol_value)
        self.b = self.b.subs(symbol_value)
        self.c = self.c.subs(symbol_value)

    def tiji(self):
        return self.a * self.b * self.c

    def duijiaoxian(self):
        return sqrt(self.a ** 2 + self.b ** 2 + self.c ** 2)


class BaseZhengSiLengZhu(BaseSiLengZhu):
    def init(self, exist_symbols):
        a = Symbol(get_symbol("a", exist_symbols))
        c = Symbol(get_symbol("c", exist_symbols))
        self.a = a
        self.b = a
        self.c = c
        return self


class BaseZhengFangTi(BaseZhengSiLengZhu):
    def init(self, exist_symbols):
        a = Symbol(get_symbol("a", exist_symbols))
        self.a = a
        self.b = a
        self.c = a
        return self


class BaseQiu(BaseLiTi):
    r = None

    def init(self, exist_symbols):
        self.r = Symbol(get_symbol("r", exist_symbols))
        return self

    def subs(self, symbol_value):
        self.r = self.r.subs(symbol_value)

    def biaomianji(self):
        return 4 * pi * self.r ** 2

    def tiji(self):
        return 3 * pi * self.r ** 2 / 4


class BaseMingTi(BaseValue):
    pass


class BaseMingTiInner(BaseValue):
    pass


class BaseNeg(BaseValue):
    pass


class BaseHypo(BaseValue):
    pass


class BaseSquare(BaseQuadra):
    pass


class BaseDiamond(BaseQuadra):
    pass


class BaseRectangle(BaseQuadra):
    pass


class BaseHengChenLi(BaseValue):
    pass


class BaseLimit(BaseExpr, Limit):  # limit(1/x, x, 0, dir="+")
    def _eval_nseries(self, x, n, logx):
        super(BaseLimit, self)._eval_nseries(x, n, logx)

    def __init__(self, *args):
        Limit.__init__(*args)
        self.expression, self.var, self.value, self.dir = self.args


if __name__ == '__main__':
    pass
